#include <stdlib.h>
#include <libavutil/log.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libavcodec/codec_par.h>
// int cutVideo(double start_time, double end_time, char *in_file_name, char *out_file_name);
int cut_video(double start_time, double end_time, char *in_file_name, char *out_file_name);

int main(int argc, char *argv[])
{
    //输入4个参数  argv1 开始时间  argv2 结束时间  argv3 输入文件  argv3 输出文件
    double start_time, end_time;
    char *in_file_name, *out_file_name;
    av_log_set_level(AV_LOG_INFO);
    if (argc < 5)
    {
        av_log(NULL, AV_LOG_ERROR, "输入参数错误");
        return -1;
    }

    start_time = atoi(argv[1]);
    end_time = atoi(argv[2]);
    in_file_name = argv[3];
    out_file_name = argv[4];
    int ret = cut_video(start_time, end_time, in_file_name, out_file_name);
    return ret;
}

int cut_video(double start_time, double end_time, char *in_file_name, char *out_file_name)
{
    int ret = 0;
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;

    //第一步获取输入上下文
    ret = avformat_open_input(&ifmt_ctx, in_file_name, NULL, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "打开输入文件失败");
        goto __ERROR;
    }
    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "获取输入文件流信息失败");

        goto __ERROR;
    }
    av_dump_format(ifmt_ctx, 0, in_file_name, 0);

    //第二步获取输出上下文
    ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_file_name);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "获取输出文件上下文失败");
        goto __ERROR;
    }

    //第三步为输出文件创建输出流
    for (int i = 0; i < ifmt_ctx->nb_streams; i++)
    {
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream)
        {
            av_log(NULL, AV_LOG_ERROR, "创建输出流失败");
            ret = AVERROR_UNKNOWN;
            goto __ERROR;
        }
        ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
        if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "copy 流参数错误");
            goto __ERROR;
        }
        out_stream->codecpar->codec_tag = 0;
    }
    av_dump_format(ofmt_ctx, 0, out_file_name, 1);

    //第三步为输出文件创建AvIOContext
    if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
    {
        ret = avio_open(&(ofmt_ctx->pb), out_file_name, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "创建输出文件IO上下文失败");
            goto __ERROR;
        }
    }
    //第四步写输出文件头
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "写多媒体文件头失败");
        goto __ERROR;
    }

    //第五步 移动输入文件的上下文到指定时间
    ret = av_seek_frame(ifmt_ctx, -1, start_time * AV_TIME_BASE, AVSEEK_FLAG_ANY);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "移动输入文件的上下文到指定时间失败");
        goto __ERROR;
    }


    //第六步读取指定时间段的数据，然后进行时间戳转化，再写入输出文件
    av_init_packet(&pkt);
    pkt.size = 0;
    pkt.data = NULL;
    while (1)
    {
        AVStream *in_stream, *out_stream;
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;
        in_stream = ifmt_ctx->streams[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];

        //如果当前时间超过了要截取的时间，直接跳出循环
        if (av_q2d(in_stream->time_base) * pkt.pts > end_time)
        {
            av_packet_unref(&pkt);
            break;
        }

        //设置输出文件的dts和pts
        av_log(NULL, AV_LOG_INFO, "writefreame pkt.pts = %lld\n", pkt.pts);

        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
        pkt.duration = (int)av_rescale_q((int64_t)pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        av_log(NULL, AV_LOG_INFO, "writefreame 22222 pkt.pts = %lld\n", pkt.pts);

        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        av_packet_unref(&pkt);
        if (ret < 0)
            break;
    }

    //第七步写多媒体文件尾巴
    av_write_trailer(ofmt_ctx);

__ERROR:
    //第八步释放资源
    if (ifmt_ctx)
    {
        avformat_close_input(&ifmt_ctx);
        avformat_free_context(ifmt_ctx);
    }
    if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
    {
        avio_closep(&(ofmt_ctx->pb));
    }

    if (ofmt_ctx)
    {
        avformat_free_context(ofmt_ctx);
    }
    return ret;
}
